LIBRARY IEEE;
USE  IEEE.STD_LOGIC_1164.all;
USE  IEEE.STD_LOGIC_ARITH.all;
USE  IEEE.STD_LOGIC_UNSIGNED.all;

ENTITY MOUSE IS


   PORT( clock, reset 			: IN std_logic;
        SIGNAL data_mouse					: INOUT std_logic;
        SIGNAL clock_mouse 					: INOUT std_logic;
        SIGNAL left_button, right_button: OUT std_logic;
		SIGNAL mouse_cursor_row 		: OUT std_logic_vector(9 DOWNTO 0); 
		SIGNAL mouse_cursor_column		: OUT std_logic_vector(9 DOWNTO 0));       
		
END MOUSE;

ARCHITECTURE behavior OF MOUSE IS

TYPE STATE_TYPE IS (INHIBIT_TRANS, LOAD_COMMAND,LOAD_COMMAND2, WAIT_OUTPUT_READY,
					WAIT_CMD_ACK, INPUT_PACKETS);

-- Signals for Mouse
SIGNAL mouse_state							: state_type;
SIGNAL inhibit_wait_count					: std_logic_vector(10 DOWNTO 0);
SIGNAL CHARIN, CHAROUT						: std_logic_vector(7 DOWNTO 0);
SIGNAL new_cursor_row, new_cursor_column 	: std_logic_vector(9 DOWNTO 0);
SIGNAL cursor_row, cursor_column 			: std_logic_vector(9 DOWNTO 0);
SIGNAL INCNT, OUTCNT, mSB_OUT 				: std_logic_vector(3 DOWNTO 0);
SIGNAL PACKET_COUNT 						: std_logic_vector(1 DOWNTO 0);
SIGNAL SHIFTIN 								: std_logic_vector(8 DOWNTO 0);
SIGNAL SHIFTOUT 							: std_logic_vector(10 DOWNTO 0);
SIGNAL PACKET_CHAR1, PACKET_CHAR2, 
		PACKET_CHAR3 						: std_logic_vector(7 DOWNTO 0); 
SIGNAL clock_mouse_BUF, DATA_READY, READ_CHAR	: std_logic;
SIGNAL i									: integer;
SIGNAL cursor, iready_set, break, toggle_next, 
		output_ready, send_char, send_data 	: std_logic;
SIGNAL data_mouse_DIR, data_mouse_OUT, data_mouse_BUF, 
		clock_mouse_DIR 						: std_logic;
SIGNAL clock_mouse_FILTER 					: std_logic;
SIGNAL filter 								: std_logic_vector(7 DOWNTO 0);


BEGIN

mouse_cursor_row <= cursor_row;
mouse_cursor_column <= cursor_column;

			-- tri_state control logic for mouse data and clock lines
data_mouse <= 'Z' WHEN data_mouse_DIR = '0' ELSE data_mouse_BUF;
clock_mouse <=  'Z' WHEN clock_mouse_DIR = '0' ELSE clock_mouse_BUF;

			-- state machine to send init command and start recv process.
	PROCESS (reset, clock)
	BEGIN
		IF reset = '1' THEN
			mouse_state <= INHIBIT_TRANS;
			inhibit_wait_count <= conv_std_logic_vector(0,11);
			SEND_DATA <= '0';
		ELSIF clock'EVENT AND clock = '1' THEN
			CASE mouse_state IS
-- Mouse powers up and sends self test codes, AA and 00 out before board is downloaded
-- Pull clock line low to inhibit any transmissions from mouse
-- Need at least 60usec to stop a transmission in progress
-- Note: This is perhaps optional since mouse should not be tranmitting
				WHEN INHIBIT_TRANS =>
				inhibit_wait_count <= inhibit_wait_count + 1;
				IF inhibit_wait_count(10 DOWNTO 9) = "11" THEN
						mouse_state <= LOAD_COMMAND;
				END IF;
					-- Enable Streaming Mode Command, F4
						charout <= "11110100";
					-- Pull data low to signal data available to mouse
				WHEN LOAD_COMMAND =>
						SEND_DATA <= '1';
						mouse_state <= LOAD_COMMAND2;
				WHEN LOAD_COMMAND2 =>
						SEND_DATA <= '1';
						mouse_state <= WAIT_OUTPUT_READY;
		-- Wait for Mouse to Clock out all bits in command.
		-- Command sent is F4, Enable Streaming Mode
		-- This tells the mouse to start sending 3-byte packets with movement data
				WHEN WAIT_OUTPUT_READY =>
					SEND_DATA <= '0';
			-- Output Ready signals that all data is clocked out of shift register
					IF OUTPUT_READY='1' THEN
						mouse_state <= WAIT_CMD_ACK;
					ELSE
						mouse_state <= WAIT_OUTPUT_READY;
					END IF;
		-- Wait for Mouse to send back Command Acknowledge, FA
				WHEN WAIT_CMD_ACK =>
					SEND_DATA <= '0';
					IF IREADY_SET='1' THEN
						mouse_state <= INPUT_PACKETS;
					END IF;
		-- Release clock and data lines and go into mouse input mode
		-- Stay in this state and recieve 3-byte mouse data packets forever
		-- Default rate is 100 packets per second
				WHEN INPUT_PACKETS =>
						mouse_state <= INPUT_PACKETS;
			END CASE;
		END IF;
	END PROCESS;

	WITH mouse_state SELECT
-- Mouse Data Tri-state control line: '1' FLEX Chip drives, '0'=Mouse Drives
		data_mouse_DIR 	<=	'0'	WHEN INHIBIT_TRANS,
							'0'	WHEN LOAD_COMMAND,
							'0'	WHEN LOAD_COMMAND2,
							'1'	WHEN WAIT_OUTPUT_READY,
							'0'	WHEN WAIT_CMD_ACK,
							'0'	WHEN INPUT_PACKETS;
-- Mouse Clock Tri-state control line: '1' FLEX Chip drives, '0'=Mouse Drives
	WITH mouse_state SELECT
		clock_mouse_DIR 	<=	'1'	WHEN INHIBIT_TRANS,
							'1'	WHEN LOAD_COMMAND,
							'1'	WHEN LOAD_COMMAND2,
							'0'	WHEN WAIT_OUTPUT_READY,
							'0'	WHEN WAIT_CMD_ACK,
							'0'	WHEN INPUT_PACKETS;
	WITH mouse_state SELECT
-- Input to FLEX chip tri-state buffer mouse clock line
		clock_mouse_BUF 	<=	'0'	WHEN INHIBIT_TRANS,
							'1'	WHEN LOAD_COMMAND,
							'1'	WHEN LOAD_COMMAND2,
							'1'	WHEN WAIT_OUTPUT_READY,
							'1'	WHEN WAIT_CMD_ACK,
							'1'	WHEN INPUT_PACKETS;

-- filter for mouse clock
PROCESS
BEGIN
		WAIT UNTIL clock'event and clock = '1';
		filter(7 DOWNTO 1) <= filter(6 DOWNTO 0);
		filter(0) <= clock_mouse;
		IF filter = "11111111" THEN
			clock_mouse_FILTER <= '1';
		ELSIF filter = "00000000" THEN
			clock_mouse_FILTER <= '0';
		END IF;
END PROCESS;

	--This process sends serial data going to the mouse
SEND_UART: PROCESS (send_data, clock_mouse_filter)
BEGIN
IF SEND_DATA = '1' THEN
	OUTCNT <= "0000";
    SEND_CHAR <= '1';
	OUTPUT_READY <= '0';
		-- Send out Start Bit(0) + Command(F4) + Parity  Bit(0) + Stop Bit(1)
	SHIFTOUT(8 DOWNTO 1) <= CHAROUT ;
		-- START BIT
	SHIFTOUT(0) <= '0';
		-- COMPUTE ODD PARITY BIT
	SHIFTOUT(9) <=  not (charout(7) xor charout(6) xor charout(5) xor 
		charout(4) xor Charout(3) xor charout(2) xor charout(1) xor 
		charout(0));
		-- STOP BIT 
	SHIFTOUT(10) <= '1';
		-- Data Available Flag to Mouse
		-- Tells mouse to clock out command data (is also start bit)
    data_mouse_BUF <= '0';

ELSIF(clock_mouse_filter'event and clock_mouse_filter='0') THEN
IF data_mouse_DIR='1' THEN
		-- SHIFT OUT NEXT SERIAL BIT
  IF SEND_CHAR = '1' THEN
		-- Loop through all bits in shift register
        IF OUTCNT <= "1001" THEN
         OUTCNT <= OUTCNT + 1;
		-- Shift out next bit to mouse
         SHIFTOUT(9 DOWNTO 0) <= SHIFTOUT(10 DOWNTO 1);
		 SHIFTOUT(10) <= '1';
         data_mouse_BUF <= SHIFTOUT(1);
		 OUTPUT_READY <= '0';
		-- END OF CHARACTER
	 ELSE
     	SEND_CHAR <= '0';
		-- Signal the character has been output
		OUTPUT_READY <= '1';
		OUTCNT <= "0000";
	END IF;
  END IF;
END IF;
END IF;
END PROCESS SEND_UART;

RECV_UART: PROCESS(reset, clock_mouse_filter)
BEGIN
IF RESET='1' THEN
	INCNT <= "0000";
    READ_CHAR <= '0';
	PACKET_COUNT <= "00";
    LEFT_BUTTON <= '0';
    RIGHT_BUTTON <= '0';
	CHARIN <= "00000000";
ELSIF clock_mouse_FILTER'event and clock_mouse_FILTER='1' THEN
	IF data_mouse_DIR='0' THEN
 		IF data_mouse='0' AND READ_CHAR='0' THEN
			READ_CHAR<= '1';
    		IREADY_SET<= '0';
 		ELSE
		-- SHIFT IN NEXT SERIAL BIT
  		IF READ_CHAR = '1' THEN
        	IF INCNT < "1001" THEN
         		INCNT <= INCNT + 1;
         		SHIFTIN(7 DOWNTO 0) <= SHIFTIN(8 DOWNTO 1);
         		SHIFTIN(8) <= data_mouse;
	 			IREADY_SET <= '0';
		-- END OF CHARACTER
	 		ELSE
	 			CHARIN <= SHIFTIN(7 DOWNTO 0);
     			READ_CHAR <= '0';
	 			IREADY_SET <= '1';
  	 			PACKET_COUNT <= PACKET_COUNT + 1;
		-- PACKET_COUNT = "00" IS ACK COMMAND
				IF PACKET_COUNT = "00" THEN
		-- Set Cursor to middle of screen
				    cursor_column <= CONV_STD_LOGIC_VECTOR(320,10);
    				cursor_row <= CONV_STD_LOGIC_VECTOR(240,10);
				    NEW_cursor_column <= CONV_STD_LOGIC_VECTOR(320,10);
    				NEW_cursor_row <= CONV_STD_LOGIC_VECTOR(240,10);
				ELSIF PACKET_COUNT = "01" THEN
					PACKET_CHAR1 <= SHIFTIN(7 DOWNTO 0);
	-- Limit Cursor on Screen Edges	
	-- Check for left screen limit
	-- All numbers are positive only, and need to check for zero wrap around.
	-- Set limits higher since mouse can move up to 128 pixels in one packet
					IF (cursor_row < 128) AND ((NEW_cursor_row > 256) OR
						(NEW_cursor_row < 2)) THEN
						cursor_row <= CONV_STD_LOGIC_VECTOR(0,10);
			-- Check for right screen limit
					ELSIF NEW_cursor_row > 480 THEN
						cursor_row <= CONV_STD_LOGIC_VECTOR(480,10);
					ELSE
						cursor_row <= NEW_cursor_row;
					END IF;
			-- Check for top screen limit
					IF (cursor_column < 128)  AND ((NEW_cursor_column > 256)  OR
						(NEW_cursor_column < 2)) THEN
						cursor_column <= CONV_STD_LOGIC_VECTOR(0,10);
			-- Check for bottom screen limit
					ELSIF NEW_cursor_column > 640 THEN
						cursor_column <= CONV_STD_LOGIC_VECTOR(640,10);
					ELSE
						cursor_column <= NEW_cursor_column;
					END IF;
  				ELSIF PACKET_COUNT = "10" THEN
					PACKET_CHAR2 <= SHIFTIN(7 DOWNTO 0);
  				ELSIF PACKET_COUNT = "11" THEN
					PACKET_CHAR3 <= SHIFTIN(7 DOWNTO 0);
  				END IF;
	 			INCNT <= conv_std_logic_vector(0,4);
  				IF PACKET_COUNT = "11" THEN
    				PACKET_COUNT <= "01";
		-- Packet Complete, so process data in packet
		-- Sign extend X AND Y two's complement motion values and 
		-- add to Current Cursor Address
		--
		-- Y Motion is Negative since up is a lower row address
    				NEW_cursor_row <= cursor_row - (PACKET_CHAR3(7) & 
								PACKET_CHAR3(7) & PACKET_CHAR3);
    				NEW_cursor_column <= cursor_column + (PACKET_CHAR2(7) & 
								PACKET_CHAR2(7) & PACKET_CHAR2);
    				LEFT_BUTTON <= PACKET_CHAR1(0);
    				RIGHT_BUTTON <= PACKET_CHAR1(1);
  				END IF;
			END IF;
  		END IF;
 	END IF;
 END IF;
END IF;
END PROCESS RECV_UART;

END behavior;